package com.detector.overflow;

import com.detector.overflow.config.DetectorConfig;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 运行时溢出监控核心类
 * 这个类会被插入到业务代码中执行
 *
 * @author detector
 */
public class OverflowMonitor {
    
    // 各种数值类型的范围
    private static final long INT_MAX = Integer.MAX_VALUE;
    private static final long INT_MIN = Integer.MIN_VALUE;
    private static final long LONG_MAX = Long.MAX_VALUE;
    private static final long LONG_MIN = Long.MIN_VALUE;
    private static final int SHORT_MAX = Short.MAX_VALUE;
    private static final int SHORT_MIN = Short.MIN_VALUE;
    private static final int BYTE_MAX = Byte.MAX_VALUE;
    private static final int BYTE_MIN = Byte.MIN_VALUE;
    
    // 配置
    private static final DetectorConfig config = DetectorConfig.getInstance();
    
    // 统计信息
    private static final ConcurrentHashMap<String, AtomicLong> warnCount = new ConcurrentHashMap<>();
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    // 日志输出
    private static PrintWriter logWriter;
    
    static {
        try {
            File logFile = new File(config.getLogFile());
            logWriter = new PrintWriter(new FileWriter(logFile, true), true);
            log("=== 溢出监控日志启动 ===");
        } catch (IOException e) {
            System.err.println("❌ 无法创建监控日志文件: " + e.getMessage());
        }
        
        // 添加关闭钩子，程序结束时打印统计
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            printStatistics();
            if (logWriter != null) {
                logWriter.close();
            }
        }));
    }
    
    /**
     * 检查整数乘法
     */
    public static void checkMultiply(int a, int b, String location) {
        // 使用long进行计算
        long result = (long) a * (long) b;
        
        // 检查是否溢出
        if (result > INT_MAX || result < INT_MIN) {
            String msg = String.format(
                "🔴 整数乘法溢出: %d * %d = %d (超出int范围) 位置: %s",
                a, b, result, location
            );
            warn(location, msg, true);
        }
        // 检查是否接近溢出（警告）
        else if (Math.abs(result) > INT_MAX * config.getThreshold()) {
            String msg = String.format(
                "⚠️  整数乘法风险: %d * %d = %d (已达阈值%.0f%%) 位置: %s",
                a, b, result, (Math.abs(result) * 100.0 / INT_MAX), location
            );
            warn(location, msg, false);
        }
    }
    
    /**
     * 检查整数加法
     */
    public static void checkAdd(int a, int b, String location) {
        long result = (long) a + (long) b;
        
        if (result > INT_MAX || result < INT_MIN) {
            String msg = String.format(
                "🔴 整数加法溢出: %d + %d = %d (超出int范围) 位置: %s",
                a, b, result, location
            );
            warn(location, msg, true);
        }
        else if (Math.abs(result) > INT_MAX * config.getThreshold()) {
            String msg = String.format(
                "⚠️  整数加法风险: %d + %d = %d (已达阈值%.0f%%) 位置: %s",
                a, b, result, (Math.abs(result) * 100.0 / INT_MAX), location
            );
            warn(location, msg, false);
        }
    }
    
    // ==================== Long 类型检测 ====================
    
    /**
     * 检查long乘法
     */
    public static void checkMultiplyLong(long a, long b, String location) {
        // 使用BigInteger进行精确计算
        try {
            long result = Math.multiplyExact(a, b);
            // 检查是否接近溢出
            if (Math.abs(result) > LONG_MAX * config.getThreshold()) {
                String msg = String.format(
                    "⚠️  long乘法风险: %d * %d = %d (已达阈值%.0f%%) 位置: %s",
                    a, b, result, (Math.abs((double)result) * 100.0 / LONG_MAX), location
                );
                warn(location, msg, false);
            }
        } catch (ArithmeticException e) {
            String msg = String.format(
                "🔴 long乘法溢出: %d * %d (超出long范围) 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查long加法
     */
    public static void checkAddLong(long a, long b, String location) {
        try {
            long result = Math.addExact(a, b);
            if (Math.abs(result) > LONG_MAX * config.getThreshold()) {
                String msg = String.format(
                    "⚠️  long加法风险: %d + %d = %d (已达阈值%.0f%%) 位置: %s",
                    a, b, result, (Math.abs((double)result) * 100.0 / LONG_MAX), location
                );
                warn(location, msg, false);
            }
        } catch (ArithmeticException e) {
            String msg = String.format(
                "🔴 long加法溢出: %d + %d (超出long范围) 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        }
    }
    
    // ==================== Float/Double 类型检测 ====================
    
    /**
     * 检查float乘法
     */
    public static void checkMultiplyFloat(float a, float b, String location) {
        float result = a * b;
        if (Float.isInfinite(result)) {
            String msg = String.format(
                "🔴 float乘法溢出: %f * %f = Infinity 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        } else if (Float.isNaN(result)) {
            String msg = String.format(
                "⚠️  float乘法异常: %f * %f = NaN 位置: %s",
                a, b, location
            );
            warn(location, msg, false);
        }
    }
    
    /**
     * 检查float加法
     */
    public static void checkAddFloat(float a, float b, String location) {
        float result = a + b;
        if (Float.isInfinite(result)) {
            String msg = String.format(
                "🔴 float加法溢出: %f + %f = Infinity 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        } else if (Float.isNaN(result)) {
            String msg = String.format(
                "⚠️  float加法异常: %f + %f = NaN 位置: %s",
                a, b, location
            );
            warn(location, msg, false);
        }
    }
    
    /**
     * 检查double乘法
     */
    public static void checkMultiplyDouble(double a, double b, String location) {
        double result = a * b;
        if (Double.isInfinite(result)) {
            String msg = String.format(
                "🔴 double乘法溢出: %f * %f = Infinity 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        } else if (Double.isNaN(result)) {
            String msg = String.format(
                "⚠️  double乘法异常: %f * %f = NaN 位置: %s",
                a, b, location
            );
            warn(location, msg, false);
        }
    }
    
    /**
     * 检查double加法
     */
    public static void checkAddDouble(double a, double b, String location) {
        double result = a + b;
        if (Double.isInfinite(result)) {
            String msg = String.format(
                "🔴 double加法溢出: %f + %f = Infinity 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        } else if (Double.isNaN(result)) {
            String msg = String.format(
                "⚠️  double加法异常: %f + %f = NaN 位置: %s",
                a, b, location
            );
            warn(location, msg, false);
        }
    }
    
    // ==================== 减法检测 ====================
    
    /**
     * 检查int减法
     */
    public static void checkSub(int a, int b, String location) {
        long result = (long) a - (long) b;
        
        if (result > INT_MAX || result < INT_MIN) {
            String msg = String.format(
                "🔴 整数减法溢出: %d - %d = %d (超出int范围) 位置: %s",
                a, b, result, location
            );
            warn(location, msg, true);
        }
        else if (Math.abs(result) > INT_MAX * config.getThreshold()) {
            String msg = String.format(
                "⚠️  整数减法风险: %d - %d = %d (已达阈值%.0f%%) 位置: %s",
                a, b, result, (Math.abs(result) * 100.0 / INT_MAX), location
            );
            warn(location, msg, false);
        }
    }
    
    /**
     * 检查long减法
     */
    public static void checkSubLong(long a, long b, String location) {
        try {
            long result = Math.subtractExact(a, b);
            if (Math.abs(result) > LONG_MAX * config.getThreshold()) {
                String msg = String.format(
                    "⚠️  long减法风险: %d - %d = %d (已达阈值%.0f%%) 位置: %s",
                    a, b, result, (Math.abs((double)result) * 100.0 / LONG_MAX), location
                );
                warn(location, msg, false);
            }
        } catch (ArithmeticException e) {
            String msg = String.format(
                "🔴 long减法溢出: %d - %d (超出long范围) 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        }
    }
    
    // ==================== 除法检测 ====================
    
    /**
     * 检查int除法 (特殊情况: Integer.MIN_VALUE / -1)
     */
    public static void checkDiv(int a, int b, String location) {
        if (a == Integer.MIN_VALUE && b == -1) {
            String msg = String.format(
                "🔴 整数除法溢出: %d / %d = 2147483648 (超出int范围) 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        }
        // 除零检测
        if (b == 0) {
            String msg = String.format(
                "🔴 除零异常: %d / 0 位置: %s",
                a, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查long除法
     */
    public static void checkDivLong(long a, long b, String location) {
        if (a == Long.MIN_VALUE && b == -1L) {
            String msg = String.format(
                "🔴 long除法溢出: %d / %d (超出long范围) 位置: %s",
                a, b, location
            );
            warn(location, msg, true);
        }
        if (b == 0L) {
            String msg = String.format(
                "🔴 除零异常: %d / 0 位置: %s",
                a, location
            );
            warn(location, msg, true);
        }
    }
    
    // ==================== 类型转换检测 ====================
    
    /**
     * 检查 long → int 转换
     */
    public static void checkL2I(long value, String location) {
        if (value > INT_MAX || value < INT_MIN) {
            int actualResult = (int) value;
            String msg = String.format(
                "🔴 类型转换溢出: long→int %d = %d (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 int → short 转换
     */
    public static void checkI2S(int value, String location) {
        if (value > SHORT_MAX || value < SHORT_MIN) {
            short actualResult = (short) value;
            String msg = String.format(
                "🔴 类型转换溢出: int→short %d = %d (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 int → byte 转换
     */
    public static void checkI2B(int value, String location) {
        if (value > BYTE_MAX || value < BYTE_MIN) {
            byte actualResult = (byte) value;
            String msg = String.format(
                "🔴 类型转换溢出: int→byte %d = %d (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 int → char 转换
     */
    public static void checkI2C(int value, String location) {
        if (value > Character.MAX_VALUE || value < Character.MIN_VALUE) {
            char actualResult = (char) value;
            String msg = String.format(
                "🔴 类型转换溢出: int→char %d = '%c' (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 double → float 转换
     */
    public static void checkD2F(double value, String location) {
        if (Math.abs(value) > Float.MAX_VALUE) {
            float actualResult = (float) value;
            String msg = String.format(
                "🔴 类型转换溢出: double→float %f = %f (精度丢失) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 double → int 转换
     */
    public static void checkD2I(double value, String location) {
        if (value > INT_MAX || value < INT_MIN) {
            int actualResult = (int) value;
            String msg = String.format(
                "🔴 类型转换溢出: double→int %f = %d (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 检查 float → int 转换
     */
    public static void checkF2I(float value, String location) {
        if (value > INT_MAX || value < INT_MIN) {
            int actualResult = (int) value;
            String msg = String.format(
                "🔴 类型转换溢出: float→int %f = %d (数据截断) 位置: %s",
                value, actualResult, location
            );
            warn(location, msg, true);
        }
    }
    
    /**
     * 记录警告
     */
    private static void warn(String location, String message, boolean isOverflow) {
        // 防止同一位置重复警告过多
        warnCount.computeIfAbsent(location, k -> new AtomicLong(0));
        long count = warnCount.get(location).incrementAndGet();
        
        // 限制警告次数
        if (count <= config.getMaxWarnings()) {
            log(message);
            // 如果是真正的溢出，总是打印到控制台
            if (isOverflow || count <= 2) {
                System.err.println(message);
            }
        } else if (count == config.getMaxWarnings() + 1) {
            String msg = String.format("💡 位置 [%s] 后续警告将被抑制（已达%d次）", 
                location, config.getMaxWarnings());
            log(msg);
        }
    }
    
    /**
     * 写入日志
     */
    private static void log(String message) {
        if (logWriter != null) {
            logWriter.println(dateFormat.format(new Date()) + " " + message);
        }
    }
    
    /**
     * 打印统计信息
     */
    private static void printStatistics() {
        String separator = repeatString("=", 60);
        
        log("\n" + separator);
        log("溢出监控统计报告");
        log(separator);
        log("总共检测到 " + warnCount.size() + " 个位置存在风险");
        
        if (!warnCount.isEmpty()) {
            log("\nTop 20 高风险位置:");
            warnCount.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue().get(), e1.getValue().get()))
                .limit(20)
                .forEach(entry -> {
                    log(String.format("  [%d次] %s", entry.getValue().get(), entry.getKey()));
                });
        }
        
        log(separator);
        log("监控已结束，详细日志保存在: " + config.getLogFile());
        log(separator + "\n");
        
        // 同时打印到控制台
        System.out.println("\n" + separator);
        System.out.println("✅ 溢出检测完成！");
        System.out.println("   检测到 " + warnCount.size() + " 个位置存在风险");
        System.out.println("   详细报告: " + config.getLogFile());
        System.out.println(separator + "\n");
    }
    
    /**
     * 重复字符串指定次数（JDK 8兼容实现）
     */
    private static String repeatString(String str, int count) {
        StringBuilder sb = new StringBuilder(str.length() * count);
        for (int i = 0; i < count; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
}
